/*
 * Copyright (C) 2007-2008 Esmertec AG.
 * Copyright (C) 2007-2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.mms.pdu;

import android.net.Uri;

import java.util.HashMap;
import java.util.Map;

/**
 * The pdu part.
 */
public class PduPart {
	/**
	 * Well-Known Parameters.
	 */
	public static final int P_Q = 0x80;
	public static final int P_CHARSET = 0x81;
	public static final int P_LEVEL = 0x82;
	public static final int P_TYPE = 0x83;
	public static final int P_DEP_NAME = 0x85;
	public static final int P_DEP_FILENAME = 0x86;
	public static final int P_DIFFERENCES = 0x87;
	public static final int P_PADDING = 0x88;
	// This value of "TYPE" s used with Content-Type: multipart/related
	public static final int P_CT_MR_TYPE = 0x89;
	public static final int P_DEP_START = 0x8A;
	public static final int P_DEP_START_INFO = 0x8B;
	public static final int P_DEP_COMMENT = 0x8C;
	public static final int P_DEP_DOMAIN = 0x8D;
	public static final int P_MAX_AGE = 0x8E;
	public static final int P_DEP_PATH = 0x8F;
	public static final int P_SECURE = 0x90;
	public static final int P_SEC = 0x91;
	public static final int P_MAC = 0x92;
	public static final int P_CREATION_DATE = 0x93;
	public static final int P_MODIFICATION_DATE = 0x94;
	public static final int P_READ_DATE = 0x95;
	public static final int P_SIZE = 0x96;
	public static final int P_NAME = 0x97;
	public static final int P_FILENAME = 0x98;
	public static final int P_START = 0x99;
	public static final int P_START_INFO = 0x9A;
	public static final int P_COMMENT = 0x9B;
	public static final int P_DOMAIN = 0x9C;
	public static final int P_PATH = 0x9D;

	/**
	 * Header field names.
	 */
	public static final int P_CONTENT_TYPE = 0x91;
	public static final int P_CONTENT_LOCATION = 0x8E;
	public static final int P_CONTENT_ID = 0xC0;
	public static final int P_DEP_CONTENT_DISPOSITION = 0xAE;
	public static final int P_CONTENT_DISPOSITION = 0xC5;
	// The next header is unassigned header, use reserved header(0x48) value.
	public static final int P_CONTENT_TRANSFER_ENCODING = 0xC8;

	/**
	 * Content=Transfer-Encoding string.
	 */
	public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";

	/**
	 * Value of Content-Transfer-Encoding.
	 */
	public static final String P_BINARY = "binary";
	public static final String P_7BIT = "7bit";
	public static final String P_8BIT = "8bit";
	public static final String P_BASE64 = "base64";
	public static final String P_QUOTED_PRINTABLE = "quoted-printable";

	/**
	 * Value of disposition can be set to PduPart when the value is octet in the
	 * PDU. "from-data" instead of Form-data<Octet 128>. "attachment" instead of
	 * Attachment<Octet 129>. "inline" instead of Inline<Octet 130>.
	 */
	static final byte[] DISPOSITION_FROM_DATA = "from-data".getBytes();
	static final byte[] DISPOSITION_ATTACHMENT = "attachment".getBytes();
	static final byte[] DISPOSITION_INLINE = "inline".getBytes();

	/**
	 * Content-Disposition value.
	 */
	public static final int P_DISPOSITION_FROM_DATA = 0x80;
	public static final int P_DISPOSITION_ATTACHMENT = 0x81;
	public static final int P_DISPOSITION_INLINE = 0x82;

	/**
	 * Header of part.
	 */
	private Map<Integer, Object> mPartHeader = null;

	/**
	 * Data uri.
	 */
	private Uri mUri = null;

	/**
	 * Part data.
	 */
	private byte[] mPartData = null;

	private static final String TAG = "PduPart";

	/**
	 * Empty Constructor.
	 */
	public PduPart() {
		mPartHeader = new HashMap<Integer, Object>();
	}

	/**
	 * Set part data. The data are stored as byte array.
	 * 
	 * @param data
	 *            the data
	 */
	public void setData(byte[] data) {
		if (data == null) {
			return;
		}

		mPartData = new byte[data.length];
		System.arraycopy(data, 0, mPartData, 0, data.length);
	}

	/**
	 * @return A copy of the part data or null if the data wasn't set or the
	 *         data is stored as Uri.
	 * @see #getDataUri
	 */
	public byte[] getData() {
		if (mPartData == null) {
			return null;
		}

		byte[] byteArray = new byte[mPartData.length];
		System.arraycopy(mPartData, 0, byteArray, 0, mPartData.length);
		return byteArray;
	}

	/**
	 * Set data uri. The data are stored as Uri.
	 * 
	 * @param uri
	 *            the uri
	 */
	public void setDataUri(Uri uri) {
		mUri = uri;
	}

	/**
	 * @return The Uri of the part data or null if the data wasn't set or the
	 *         data is stored as byte array.
	 * @see #getData
	 */
	public Uri getDataUri() {
		return mUri;
	}

	/**
	 * Set Content-id value
	 * 
	 * @param contentId
	 *            the content-id value
	 * @throws NullPointerException
	 *             if the value is null.
	 */
	public void setContentId(byte[] contentId) {
		if ((contentId == null) || (contentId.length == 0)) {
			throw new IllegalArgumentException("Content-Id may not be null or empty.");
		}

		if ((contentId.length > 1) && ((char) contentId[0] == '<') && ((char) contentId[contentId.length - 1] == '>')) {
			mPartHeader.put(P_CONTENT_ID, contentId);
			return;
		}

		// Insert beginning '<' and trailing '>' for Content-Id.
		byte[] buffer = new byte[contentId.length + 2];
		buffer[0] = (byte) (0xff & '<');
		buffer[buffer.length - 1] = (byte) (0xff & '>');
		System.arraycopy(contentId, 0, buffer, 1, contentId.length);
		mPartHeader.put(P_CONTENT_ID, buffer);
	}

	/**
	 * Get Content-id value.
	 * 
	 * @return the value
	 */
	public byte[] getContentId() {
		return (byte[]) mPartHeader.get(P_CONTENT_ID);
	}

	/**
	 * Set Char-set value.
	 * 
	 * @param charset
	 *            the value
	 */
	public void setCharset(int charset) {
		mPartHeader.put(P_CHARSET, charset);
	}

	/**
	 * Get Char-set value
	 * 
	 * @return the charset value. Return 0 if charset was not set.
	 */
	public int getCharset() {
		Integer charset = (Integer) mPartHeader.get(P_CHARSET);
		if (charset == null) {
			return 0;
		} else {
			return charset.intValue();
		}
	}

	/**
	 * Set Content-Location value.
	 * 
	 * @param contentLocation
	 *            the value
	 * @throws NullPointerException
	 *             if the value is null.
	 */
	public void setContentLocation(byte[] contentLocation) {
		if (contentLocation == null) {
			throw new NullPointerException("null content-location");
		}

		mPartHeader.put(P_CONTENT_LOCATION, contentLocation);
	}

	/**
	 * Get Content-Location value.
	 * 
	 * @return the value return PduPart.disposition[0] instead of <Octet 128>
	 *         (Form-data). return PduPart.disposition[1] instead of <Octet 129>
	 *         (Attachment). return PduPart.disposition[2] instead of <Octet
	 *         130> (Inline).
	 */
	public byte[] getContentLocation() {
		return (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
	}

	/**
	 * Set Content-Disposition value. Use PduPart.disposition[0] instead of
	 * <Octet 128> (Form-data). Use PduPart.disposition[1] instead of <Octet
	 * 129> (Attachment). Use PduPart.disposition[2] instead of <Octet 130>
	 * (Inline).
	 * 
	 * @param contentDisposition
	 *            the value
	 * @throws NullPointerException
	 *             if the value is null.
	 */
	public void setContentDisposition(byte[] contentDisposition) {
		if (contentDisposition == null) {
			throw new NullPointerException("null content-disposition");
		}

		mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition);
	}

	/**
	 * Get Content-Disposition value.
	 * 
	 * @return the value
	 */
	public byte[] getContentDisposition() {
		return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION);
	}

	/**
	 * Set Content-Type value.
	 * 
	 * @param value
	 *            the value
	 * @throws NullPointerException
	 *             if the value is null.
	 */
	public void setContentType(byte[] contentType) {
		if (contentType == null) {
			throw new NullPointerException("null content-type");
		}

		mPartHeader.put(P_CONTENT_TYPE, contentType);
	}

	/**
	 * Get Content-Type value of part.
	 * 
	 * @return the value
	 */
	public byte[] getContentType() {
		return (byte[]) mPartHeader.get(P_CONTENT_TYPE);
	}

	/**
	 * Set Content-Transfer-Encoding value
	 * 
	 * @param contentId
	 *            the content-id value
	 * @throws NullPointerException
	 *             if the value is null.
	 */
	public void setContentTransferEncoding(byte[] contentTransferEncoding) {
		if (contentTransferEncoding == null) {
			throw new NullPointerException("null content-transfer-encoding");
		}

		mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding);
	}

	/**
	 * Get Content-Transfer-Encoding value.
	 * 
	 * @return the value
	 */
	public byte[] getContentTransferEncoding() {
		return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING);
	}

	/**
	 * Set Content-type parameter: name.
	 * 
	 * @param name
	 *            the name value
	 * @throws NullPointerException
	 *             if the value is null.
	 */
	public void setName(byte[] name) {
		if (null == name) {
			throw new NullPointerException("null content-id");
		}

		mPartHeader.put(P_NAME, name);
	}

	/**
	 * Get content-type parameter: name.
	 * 
	 * @return the name
	 */
	public byte[] getName() {
		return (byte[]) mPartHeader.get(P_NAME);
	}

	/**
	 * Get Content-disposition parameter: filename
	 * 
	 * @param fileName
	 *            the filename value
	 * @throws NullPointerException
	 *             if the value is null.
	 */
	public void setFilename(byte[] fileName) {
		if (null == fileName) {
			throw new NullPointerException("null content-id");
		}

		mPartHeader.put(P_FILENAME, fileName);
	}

	/**
	 * Set Content-disposition parameter: filename
	 * 
	 * @return the filename
	 */
	public byte[] getFilename() {
		return (byte[]) mPartHeader.get(P_FILENAME);
	}

	public String generateLocation() {
		// Assumption: At least one of the content-location / name / filename
		// or content-id should be set. This is guaranteed by the PduParser
		// for incoming messages and by MM composer for outgoing messages.
		byte[] location = (byte[]) mPartHeader.get(P_NAME);
		if (null == location) {
			location = (byte[]) mPartHeader.get(P_FILENAME);

			if (null == location) {
				location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
			}
		}

		if (null == location) {
			byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID);
			return "cid:" + new String(contentId);
		} else {
			return new String(location);
		}
	}
}
